package com.ideaaedi.springcloud.jd.commonspring.mybatisplus.tenant;

import com.google.common.collect.Sets;
import com.ideaaedi.commonds.function.NoArgConsumer;
import com.ideaaedi.commonds.function.NoArgFunction;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Set;

/**
 * 临时租户提供器
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2021.0.5.B
 */
public final class TenantProvider {
    
    /**
     * 线程级租户信息.
     * <pre>
     *     格式为: 租户id | 可读数据范围json | 可写数据范围json
     * </pre>
     */
    public static final ThreadLocal<TenantDataScopeBO> TENANT_HOLDER = new ThreadLocal<>();
    
    /**
     * （全局）执行并返回结果
     * <p>
     * 注：全局只对改查删有效
     *
     * @see TenantProvider#exec(NoArgFunction, String, Collection, Collection)
     */
    public static <R> R execGlobal(NoArgFunction<R> function) {
        return TenantProvider.exec(function, TenantDataScopeBO.IGNORE_URD, null, null);
    }
    
    /**
     * 执行并返回结果
     * <p>
     * 注：数据读写范围与租户值保持一致
     * <p>
     * @see TenantProvider#exec(NoArgFunction, String, Collection, Collection)
     */
    public static <R> R exec(NoArgFunction<R> function, @NonNull String tenant) {
        Set<String> dataScopePaths = Sets.newHashSet(tenant);
        return TenantProvider.exec(function, tenant, dataScopePaths, dataScopePaths);
    }
    
    /**
     * （全局）执行并返回结果
     * <p>
     * 注：全局只对改查删有效
     *
     * @see TenantProvider#voidExec(NoArgConsumer, String, Collection, Collection)
     */
    public static void voidExecGlobal(NoArgConsumer consumer) {
        TenantProvider.voidExec(consumer, TenantDataScopeBO.IGNORE_URD, null, null);
    }
    
    /**
     * 执行并返回结果.
     * <p>
     * 注：数据读写范围与租户值保持一致
     * <p>
     * @see TenantProvider#voidExec(NoArgConsumer, String, Collection, Collection)
     */
    public static void exec(NoArgConsumer consumer, @NonNull String tenant) {
        Set<String> dataScopePaths = Sets.newHashSet(tenant);
        TenantProvider.voidExec(consumer, tenant, dataScopePaths, dataScopePaths);
    }
    
    /**
     * 执行并返回结果
     *
     * @param function 代码逻辑
     * @param tenant 指定的租户值
     */
    public static <R> R exec(NoArgFunction<R> function, @NonNull String tenant,
                             @Nullable Collection<String> readDataScopePaths,
                             @Nullable Collection<String> updateDataScopePaths) {
        try {
            Assert.isTrue(StringUtils.isNotBlank(tenant), "tenant cannot be blank.");
            TenantDataScopeBO tenantDataScopeBO = TENANT_HOLDER.get();
            String tenantDataScopeStr = TenantDataScopeBO.buildTenantDataScopeStr(tenant, readDataScopePaths, updateDataScopePaths);
            if (tenantDataScopeBO == null) {
                TENANT_HOLDER.set(new TenantDataScopeBO(tenantDataScopeStr));
            } else {
                tenantDataScopeBO.getTenantDeque().addFirst(tenantDataScopeStr);
            }
            return function.apply();
        } finally {
            TenantDataScopeBO tenantDataScopeBO = TENANT_HOLDER.get();
            if (tenantDataScopeBO != null) {
                tenantDataScopeBO.getTenantDeque().pollFirst();
                if (tenantDataScopeBO.getTenantDeque().size() <= 0) {
                    TENANT_HOLDER.remove();
                }
            }
        }
    }
    
    /**
     * 执行
     *
     * @param consumer 代码逻辑
     * @param tenant 指定的租户值
     * @param readDataScopePaths 可读数据范围的deptId对应的deptPath
     * @param updateDataScopePaths 可写(修改/删除)数据范围的deptId对应的deptPath
     */
    public static void voidExec(NoArgConsumer consumer, @NonNull String tenant,
                                @Nullable Collection<String> readDataScopePaths,
                                @Nullable Collection<String> updateDataScopePaths) {
        try {
            Assert.isTrue(StringUtils.isNotBlank(tenant), "tenant cannot be blank.");
            TenantDataScopeBO tenantDataScopeBO = TENANT_HOLDER.get();
            String tenantDataScopeStr = TenantDataScopeBO.buildTenantDataScopeStr(tenant, readDataScopePaths, updateDataScopePaths);
            if (tenantDataScopeBO == null) {
                TENANT_HOLDER.set(new TenantDataScopeBO(tenantDataScopeStr));
            } else {
                tenantDataScopeBO.getTenantDeque().addFirst(tenantDataScopeStr);
            }
            consumer.accept();
        } finally {
            TenantDataScopeBO tenantDataScopeBO = TENANT_HOLDER.get();
            if (tenantDataScopeBO != null) {
                tenantDataScopeBO.getTenantDeque().pollFirst();
                if (tenantDataScopeBO.getTenantDeque().size() <= 0) {
                    TENANT_HOLDER.remove();
                }
            }
        }
    }
    
}
